using AdminApiTemplate.Common.Models;
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AdminApiTemplate.Common.Helpers
{
    /// <summary>
    /// 
    /// </summary>
    public static class MapperHelper
    {
        #region 公共方法

        /// <summary>
        /// 初始（静态方法）
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        private static class MapperInitialize<TSource, TDestination>
        {
            public readonly static IMapper mapper = Initialize();

            private static AutoMapper.IMapper Initialize()
            {
                var sourceType = typeof(TSource);
                var destinationType = typeof(TDestination);

                var config = new AutoMapper.MapperConfiguration(cfg =>
                    //cfg.CreateMap<TSource, TDestination>().ReverseMap().IncludeAllDerived().PreserveReferences().ConstructUsingServiceLocator()
                    MyCreateMap(cfg, sourceType, destinationType)
                    );
                var mapper = config.CreateMapper();
                return mapper;
            }

            /// <summary>
            /// 判断子对象
            /// </summary>
            /// <param name="cfg"></param>
            /// <param name="sourceType"></param>
            /// <param name="destinationType"></param>
            private static void MyCreateMap(AutoMapper.IMapperConfigurationExpression cfg, Type sourceType, Type destinationType)
            {
                cfg.CreateMap(sourceType, destinationType);
                foreach (var destinationP in destinationType.GetProperties())
                {
                    if (!destinationP.CanWrite)
                        continue;

                    var sourceP = sourceType.GetProperty(destinationP.Name);
                    bool isExist = sourceP != null;
                    if (!isExist || !sourceP.CanRead)//说明输出类型中该字段在输入类型中不存在
                    {
                        continue;
                    }

                    //是否为Class
                    if (destinationP.PropertyType != sourceP.PropertyType
                        && destinationP.PropertyType.IsClass && sourceP.PropertyType.IsClass
                        )
                    {
                        //过滤数组
                        var sourcePType = GetEnumerableType(sourceP.PropertyType);
                        var destinationPType = GetEnumerableType(destinationP.PropertyType);
                        //是否为Class
                        if (sourcePType != null && destinationPType != null
                            && sourcePType != destinationPType
                            && sourcePType.IsClass && destinationPType.IsClass
                            )
                        {
                            MyCreateMap(cfg, sourcePType, destinationPType);
                        }
                    }
                }
            }

            /// <summary>
            /// 判断过滤数组
            /// </summary>
            /// <param name="type"></param>
            /// <returns></returns>
            private static Type GetEnumerableType(Type type)
            {
                if (type.IsArray)
                {
                    string tName = type.FullName.Replace("[]", string.Empty);
                    var resType = type.Assembly.GetType(tName);
                    return GetEnumerableType(resType);
                }
                if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type))
                {
                    var resType = type.GetGenericArguments()[0];
                    return GetEnumerableType(resType);
                }
                return type;
            }
        }
        /// <summary>
        /// 初始
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <returns></returns>
        private static AutoMapper.IMapper CreateMapper<TSource, TDestination>()
        {
            return MapperInitialize<TSource, TDestination>.mapper;
        }
        #endregion

        /// <summary>
        /// Mapper覆盖
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="destination"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static TDestination ToMapperCover<TSource, TDestination>(this TDestination destination, TSource source)
        {
            var mapper = CreateMapper<TSource, TDestination>();
            destination = mapper.Map(source, destination);
            return destination;
        }

        /// <summary>
        /// 转换Mapper
        /// </summary>
        /// <returns></returns>
        public static TDestination ToMapper<TSource, TDestination>(this TSource source)
        {
            var mapper = CreateMapper<TSource, TDestination>();
            var model = mapper.Map<TSource, TDestination>(source);
            return model;
        }

        /// <summary>
        /// 转换Mappers列表
        /// </summary>
        /// <returns></returns>
        public static List<TDestination> ToMapperList<TSource, TDestination>(this IList<TSource> source)
        {
            var mapper = CreateMapper<TSource, TDestination>();
            var model = mapper.Map<List<TDestination>>(source);
            return model;
        }

        #region toObject
        ///// <summary>
        ///// 缓存
        ///// </summary>
        //private readonly static ConcurrentDictionary<string, AutoMapper.IMapper> dicCache = new ConcurrentDictionary<string, AutoMapper.IMapper>();
        /// <summary>
        /// 初始(Type)
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <returns></returns>
        private static AutoMapper.IMapper CreateMapper<TDestination>(Type sourceType)
        {
            var destinationType = typeof(TDestination);

            //string key = $"{sourceType.FullName},{destinationType.FullName}";
            //if (dicCache.TryGetValue(key, out AutoMapper.IMapper value))
            //{
            //    return value;
            //}
            var config = new AutoMapper.MapperConfiguration(cfg =>
                    cfg.CreateMap(sourceType, destinationType));
            var mapper = config.CreateMapper();

            //return dicCache.GetOrAdd(key, mapper);
            return mapper;
        }
        /// <summary>
        /// 转换Mapper
        /// </summary>
        /// <returns></returns>
        public static TDestination ToMapper<TDestination>(this object source)
        {
            var mapper = CreateMapper<TDestination>(source.GetType());
            var model = mapper.Map<TDestination>(source);

            return model;
        }
        /// <summary>
        /// 转换Mappers列表
        /// </summary>
        /// <returns></returns>
        public static List<TDestination> ToMapperList<TDestination>(this IEnumerable<object> source)
        {
            var mapper = CreateMapper<TDestination>(source.FirstOrDefault().GetType());
            var model = mapper.Map<List<TDestination>>(source);

            return model;
        }
        #endregion
    }
}
